#include <pthread.h>
#include "wrap.h"

#define SERV_PORT 8888
#define MAXSIZE 4096

/* 这是一个由子线程来实现高并发的服务器*/

// 创建客户端的地址结构
// 定义一个结构体，将地址结构和clit_fd绑定
struct s_info{
	struct sockaddr_in clit_addr;	
	int clit_fd;
};
	
// 子线程处理函数
void *doWork(void *arg){
	// 这是一个子线程处理函数，用于客户端与服务器进行通信
	char buf[MAXSIZE], clit_addr_buf[1024];
	int ret, i;

	struct s_info *pthread_clit = (struct s_info*)arg;

	// 使用read和write函数进行读写
	while(1){
		ret = Read(pthread_clit->clit_fd, buf, MAXSIZE);
		
		if(ret == 0){
			printf("the client %d closed...\n", pthread_clit->clit_fd);
			break;
		}
		
		// 打印IP地址和端口号
		printf("%s, %d的通信内容为：\n", inet_ntop(AF_INET, &(pthread_clit->clit_addr.sin_addr.s_addr), clit_addr_buf, sizeof(clit_addr_buf)),
			       	ntohs(pthread_clit->clit_addr.sin_port));

		Write(STDOUT_FILENO, buf, ret); 	// 写出到屏幕
		for(i=0; i<ret; ++i){
			buf[i] = toupper(buf[i]);
		}
		Write(pthread_clit->clit_fd, buf, ret); // 写回给客户端
	}
	// 如果客户端关闭TCP连接，则关闭子线程文件描述符
	close(pthread_clit->clit_fd);
	
	// 退出子线程
	pthread_exit(0);
		
}


// 服务器程序
void pthread_server(){
	// 创建服务器和客户段的文件描述符和地址结构
	int serv_fd, clit_fd;
	struct sockaddr_in serv_addr, clit_addr;
	int i=0;
        socklen_t clit_addr_len;	
	
	// 创建子线程
	pthread_t tid;


	struct s_info clit[256];
	// TCP服务器创建步骤：
	// 1. 创建socket套接字
	serv_fd = Socket(AF_INET, SOCK_STREAM, 0);
	
	// 2. 使用bind函数进行绑定
	bzero(&serv_addr, sizeof(serv_addr));
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_port = htons(SERV_PORT);
	serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);

	// 为了避免服务器重启时需要等待2MSL时长，可以设置服务器的端口复用
	int opt = 1;	// 设置端口复用为1
	setsockopt(serv_fd, SOL_SOCKET, SO_REUSEADDR, (void*)&opt, sizeof(opt));

	Bind(serv_fd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));

	// 3. 使用listen函数创建最大连接数量
	Listen(serv_fd, MAXSIZE);
	
	printf("服务器进入监听阻塞状态!\n");
	clit_addr_len = sizeof(clit_addr);
	// 4. 使用accept函数进行监听
	while(1){
		clit_fd = Accept(serv_fd, (struct sockaddr*)&clit_addr, &clit_addr_len);
		clit[i].clit_fd = clit_fd;
		clit[i].clit_addr = clit_addr;

		// 5. 创建子线程，使客户端与服务器进行通信
		// 创建子线程，每个客户端由一个子线程处理
		pthread_create(&tid, NULL, doWork, (void*)&clit[i]);
		pthread_detach(tid);		// 子线程分离，避免僵尸进程产生
		i++;
	}
}


int main(){
	pthread_server();
	return 0;
}
